#include "fastcgi.h"
namespace
{
    uint16_t readInt16(const void *p)
    {
        uint16_t be16 = 0;
        ::memcpy(&be16, p, sizeof(be16));
        return yhaida::sockets::networkToHost16(be16);
    }
}

using namespace yhaida;

// const unsigned FastCgiCodec::kRecordHeader = static_cast<unsigned>(sizeof(FastCgiCodec::RecordHeader));

bool FastCgiCodec::parseRequest(Buffer *buffer)
{
    while (buffer->readableBytes() >= kRecordHeader)
    {
        FastCgiCodec::RecordHeader header;
        memcpy(&header, buffer->peek(), kRecordHeader);
        header.id = sockets::networkToHost16(header.id);
        header.length = sockets::networkToHost16(header.length);

        size_t len_total = kRecordHeader + header.length + header.padding;
        if (buffer->readableBytes() >= len_total)
        {
            switch (header.type)
            {
            case kFcgiBeginRequest:
                onBeginRequest(header, buffer);
                break;
            case kFcgiParams:
                onParams(buffer->peek() + kRecordHeader, header.length);
                break;
            case kFcgiStdin:
                onStdin(buffer->peek() + kRecordHeader, header.length);
                break;
            case kFcgiData:
                // TODO
                break;
            case kFcgiGetValues:
                // TODO
                break;
            default:
                // TODO
                break;
            }
            buffer->retrieve(len_total);
        }
        else
        {
            break;
        }
    }
    return true;
}

void FastCgiCodec::onMessage(const TcpConnectionPtr &conn,
                             Buffer *buffer,
                             Timestamp recieveTime)
{
    LOG_DEBUG;
    parseRequest(buffer);
    if (_gotRequest)
    {
        LOG_DEBUG;
        /**
         * @brief   调用回调函数
         * @param _params 参数哈希表
         * @param _stdin 输入缓存
         */
        _cb(conn, _params, &_stdin);

        //清空
        _stdin.retrieveAll();
        _paramsStream.retrieveAll();
        _params.clear();
        _gotRequest = false;
        if (!_keepConn)
        {
            LOG_DEBUG<<"*************************";
            conn->shutdown();
        }
    }
}

void FastCgiCodec::respond(Buffer *response)
{
    if (response->readableBytes() < 65536 &&
        response->prependableBytes() >= kRecordHeader)
    {
        RecordHeader header =
            {
                1,                                                                          // version
                kFcgiStdout,                                                                // type
                sockets::hostToNetwork16(1),                                                // id
                sockets::hostToNetwork16(static_cast<uint16_t>(response->readableBytes())), // length
                static_cast<uint8_t>(-response->readableBytes() & 7),                       // padding
                0                                                                           // unused
            };
        response->prepend(&header, kRecordHeader);
        response->append("\0\0\0\0\0\0\0\0", header.padding);
    }
    else
    {
    }

    endStdout(response);
    endRequest(response);
}

bool FastCgiCodec::onBeginRequest(const RecordHeader &header, const Buffer *buffer)
{
    assert(buffer->readableBytes() >= header.length);
    assert(header.type == kFcgiBeginRequest);
    if (buffer->readableBytes() >= kRecordHeader) //比下一个包头更大->有message
    {
        uint16_t role = readInt16(buffer->peek() + kRecordHeader);
        uint8_t flags = buffer->peek()[kRecordHeader + sizeof(int16_t)]; // 8+2
        if (role == kFcgiResponder)
        {
            _keepConn = flags == kFcgiKeepConn;
            return true;
        }
    }
    return false;
}
void FastCgiCodec::onStdin(const char *content, uint16_t length)
{
    if (length > 0)
    {
        _stdin.append(content, length);
    }
    else
    {
        _gotRequest = true;
    }
}
bool FastCgiCodec::onParams(const char *content, uint16_t length)
{
    if (length > 0)
    {
        _paramsStream.append(content, length);
    }
    else if (!parseAllParams())
    {
        LOG_ERROR << "parseAllParams() failed";
        return false;
    }
    return true;
}
bool FastCgiCodec::parseAllParams()
{
    while (_paramsStream.readableBytes() > 0)
    {
        LOG_TRACE;
        std::string name;
        std::string value;

        const char *crlf = _paramsStream.findCRLF();
        if (crlf)
        {
            name = std::string(_paramsStream.peek(), crlf - _paramsStream.peek());
            LOG_INFO<<name;
            _paramsStream.retrieveUntil(crlf + 2);
        }
        crlf = _paramsStream.findCRLF();
        if (crlf)
        {
            value = std::string(_paramsStream.peek(), crlf - _paramsStream.peek());
            LOG_INFO<<value;
            _paramsStream.retrieveUntil(crlf + 2);
        }
        else
        {
            return false;
        }
        _params[name] = value;
    }
    return true;
}

void FastCgiCodec::endStdout(Buffer *buffer)
{
    RecordHeader header =
        {
            1,                           // version
            kFcgiStdout,                 // type
            sockets::hostToNetwork16(1), // id
            0,                           // length
            0,                           // padding
            0,                           // unused
        };
    buffer->append(&header, kRecordHeader);
}

void FastCgiCodec::endRequest(Buffer *buffer)
{
    RecordHeader header =
        {
            1,                                       // version
            kFcgiEndRequest,                         // type
            sockets::hostToNetwork16(1),             // id
            sockets::hostToNetwork16(kRecordHeader), // length
            0,                                       // padding
            0,                                       // unused
        };
    buffer->append(&header, kRecordHeader);
    buffer->appendInt32(0);
    buffer->appendInt32(0);
}